package com.ifi.app.photofun;

import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Shader.TileMode;


public class ImageProcessor {
	
    
    
//    static {
//	    if (!OpenCVLoader.initDebug()) {
//	        // Handle initialization error
//	    	Toast.makeText(null, "Error!", Toast.LENGTH_SHORT).show();
//	    }
//	}
    
    public static Bitmap createSepiaToningEffect(Bitmap src, int depth, double red, double green, double blue) {
		// image size
		int width = src.getWidth();
		int height = src.getHeight();
		// create output bitmap
		Bitmap bmOut = Bitmap.createBitmap(width, height, src.getConfig());
		// constant grayscale
		final double GS_RED = 0.3;
		final double GS_GREEN = 0.59;
		final double GS_BLUE = 0.11;
		// color information
		int A, R, G, B;
		int pixel;

		// scan through all pixels
		for(int x = 0; x < width; ++x) {
			for(int y = 0; y < height; ++y) {
				// get pixel color
				pixel = src.getPixel(x, y);
				// get color on each channel
				A = Color.alpha(pixel);
				R = Color.red(pixel);
				G = Color.green(pixel);
				B = Color.blue(pixel);
				// apply grayscale sample
				B = G = R = (int)(GS_RED * R + GS_GREEN * G + GS_BLUE * B);

				// apply intensity level for sepid-toning on each channel
				R += (depth * red);
				if(R > 255) { R = 255; }

				G += (depth * green);
				if(G > 255) { G = 255; }

				B += (depth * blue);
				if(B > 255) { B = 255; }

				// set new pixel color to output image
				bmOut.setPixel(x, y, Color.argb(A, R, G, B));
			}
		}
		// return final image
		return bmOut;
	}
    
    /** 
     * It's used for water mark, it makes a text on the image
     * pas encore bon
     */
    public static Bitmap waterMark(Bitmap src, String watermark, Point location, int color, int alpha, int size, boolean underline) {
        int w = src.getWidth();
        int h = src.getHeight();
        Bitmap result = Bitmap.createBitmap(w, h, src.getConfig());
     
        Canvas canvas = new Canvas(result);
        canvas.drawBitmap(src, 0, 0, null);
     
        Paint paint = new Paint();
        paint.setColor(color);
        paint.setAlpha(alpha);
        paint.setTextSize(size);
        paint.setAntiAlias(true);
        paint.setUnderlineText(underline);
        canvas.drawText(watermark, location.x, location.y, paint);
     
        return result;
    }
    
    public static Bitmap applyGaussianBlur(Bitmap src) {
        double[][] GaussianBlurConfig = new double[][] {
            { 1, 2, 1 },
            { 2, 4, 2 },
            { 1, 2, 1 }
        };
        ConvolutionMatrix convMatrix = new ConvolutionMatrix(3);
        convMatrix.applyConfig(GaussianBlurConfig);
        convMatrix.Factor = 16;
        convMatrix.Offset = 0;
        return ConvolutionMatrix.computeConvolution3x3(src, convMatrix);
    }
    
    public static Bitmap doGreyscale(Bitmap src) {
        // constant factors
        final double GS_RED = 0.299;
        final double GS_GREEN = 0.587;
        final double GS_BLUE = 0.114;
     
        // create output bitmap
        Bitmap bmOut = Bitmap.createBitmap(src.getWidth(), src.getHeight(), src.getConfig());
        // pixel information
        int A, R, G, B;
        int pixel;
     
        // get image size
        int width = src.getWidth();
        int height = src.getHeight();
     
        // scan through every single pixel
        for(int x = 0; x < width; ++x) {
            for(int y = 0; y < height; ++y) {
                // get one pixel color
                pixel = src.getPixel(x, y);
                // retrieve color of all channels
                A = Color.alpha(pixel);
                R = Color.red(pixel);
                G = Color.green(pixel);
                B = Color.blue(pixel);
                // take conversion up to one single value
                R = G = B = (int)(GS_RED * R + GS_GREEN * G + GS_BLUE * B);
                // set new pixel color to output bitmap
                bmOut.setPixel(x, y, Color.argb(A, R, G, B));
            }
        }
     
        // return final image
        return bmOut;
    }
    
    public static Bitmap doInvert(Bitmap src) {
        // create new bitmap with the same settings as source bitmap
        Bitmap bmOut = Bitmap.createBitmap(src.getWidth(), src.getHeight(), src.getConfig());
        // color info
        int A, R, G, B;
        int pixelColor;
        // image size
        int height = src.getHeight();
        int width = src.getWidth();
     
        // scan through every pixel
        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                // get one pixel
                pixelColor = src.getPixel(x, y);
                // saving alpha channel
                A = Color.alpha(pixelColor);
                // inverting byte for each R/G/B channel
                R = 255 - Color.red(pixelColor);
                G = 255 - Color.green(pixelColor);
                B = 255 - Color.blue(pixelColor);
                // set newly-inverted pixel to output image
                bmOut.setPixel(x, y, Color.argb(A, R, G, B));
            }
        }
     
        // return final bitmap
        return bmOut;
    }
    
    public static Bitmap applyReflection(Bitmap src) {
        // gap space between original and reflected
        final int reflectionGap = 10;
        // get image size
        int width = src.getWidth();
        int height = src.getHeight();
     
        // this will not scale but will flip on the Y axis
        Matrix matrix = new Matrix();
        matrix.preScale(1, -1);
           
        // create a Bitmap with the flip matrix applied to it.
        // we only want the bottom half of the image
        Bitmap reflectionImage = Bitmap.createBitmap(src, 0, height/2, width, height/2, matrix, false);         
               
        // create a new bitmap with same width but taller to fit reflection
        Bitmap bitmapWithReflection = Bitmap.createBitmap(width, (height + height/2), Config.ARGB_8888);
         
        // create a new Canvas with the bitmap that's big enough for
        // the image plus gap plus reflection
        Canvas canvas = new Canvas(bitmapWithReflection);
        // draw in the original image
        canvas.drawBitmap(src, 0, 0, null);
        // draw in the gap
        Paint defaultPaint = new Paint();
        canvas.drawRect(0, height, width, height + reflectionGap, defaultPaint);
        // draw in the reflection
        canvas.drawBitmap(reflectionImage,0, height + reflectionGap, null);
          
        // create a shader that is a linear gradient that covers the reflection
        Paint paint = new Paint();
        LinearGradient shader = new LinearGradient(0, src.getHeight(), 0,
                bitmapWithReflection.getHeight() + reflectionGap, 0x70ffffff, 0x00ffffff,
                TileMode.CLAMP);
        // set the paint to use this shader (linear gradient)
        paint.setShader(shader);
        // set the Transfer mode to be porter duff and destination in
        paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
        // draw a rectangle using the paint with our linear gradient
        canvas.drawRect(0, height, width, bitmapWithReflection.getHeight() + reflectionGap, paint);

        return bitmapWithReflection;
    }
    
    /** 
     * Create a fish eye effect to a Bitmap image
     */
	public static Bitmap doFishEye(Bitmap src) {
		double r; // radius of fish eye
		int w; // weight
		int vx, vy;
		double rp;
		// create new bitmap with the same settings as source bitmap
		Bitmap bmOut = Bitmap.createBitmap(src.getWidth(), src.getHeight(),
				src.getConfig());
		// color info
		int pixelColor;
		// image size
		int height = src.getHeight();
		int width = src.getWidth();
System.out.println("Height: " + height+ " width: " + width);
		w = 10;
		r = ((width) < (height) ? (width) : (height)) / 2;
long t1 = System.currentTimeMillis();
		// scan through every pixel
		for (int y = 0; y < height; y++) {
			for (int x = 0; x < width; x++) {

				rp = Math.sqrt((double) (w * w
						+ Math.pow((double) (x - width / 2), 2) + Math.pow(
						(double) (y - height / 2), 2)));

				vx = (int) ((rp * (x - width / 2)) / r + width / 2);
				vy = (int) ((rp * (y - height / 2)) / r + height / 2);

				if (0 <= vx && vx < width && 0 <= vy && vy < height) {
					// get one pixel
					pixelColor = src.getPixel(vx, vy);
					// set newly-inverted pixel to output image
					bmOut.setPixel(x, y, pixelColor);
				}

			}
		}
long t2 = System.currentTimeMillis();
System.out.println("Fisheye time: " + (t2 - t1));
		// return final bitmap
		return bmOut;
	}
    
	/** 
     * Create a fish eye effect to a Bitmap image
     * with a point in the image (ix, iy )
     */
	public static Bitmap doFishEye(Bitmap src, int ix, int iy, double radius) {
		double r = radius; // radius of fish eye
		int w; // weight
		int vx, vy;
		double rp;
		// create new bitmap with the same settings as source bitmap
		Bitmap bmOut = Bitmap.createBitmap(src.getWidth(), src.getHeight(),
				src.getConfig());
		// color info
		int pixelColor;
		// image size
		int height = src.getHeight();
		int width = src.getWidth();
System.out.println("Height: " + height+ " width: " + width);
		w = 20;
//		r = ((width) < (height) ? (width) : (height)) / 2;
long t1 = System.currentTimeMillis();

		// scan through every pixel
		for (int y = 0; y < height; y++) {
			for (int x = 0; x < width; x++) {
				
				//calcul if (x,y) is in the circle (I, radius)
				if (r * r >= (x - ix) * (x - ix) + (y - iy) * (y - iy)) {
					rp = Math.sqrt((double) (w * w
							+ Math.pow((double) (x - ix), 2) + Math.pow(
							(double) (y - iy), 2)));

					vx = (int) ((rp * (x - ix)) / r + ix);
					vy = (int) ((rp * (y - iy)) / r + iy);

					if (0 <= vx && vx < width && 0 <= vy && vy < height) {
						// get one pixel
						pixelColor = src.getPixel(vx, vy);
						// set newly-inverted pixel to output image
						bmOut.setPixel(x, y, pixelColor);
					}
				} else {
					bmOut.setPixel(x, y, src.getPixel(x, y));
				}
			}
		}
	
long t2 = System.currentTimeMillis();
System.out.println("Fisheye with point time: " + (t2 - t1));
		// return final bitmap
		return bmOut;
	}
	
	
	/** 
     * Create a fish eye effect to a Bitmap image
     * with a point in the image (ix, iy )
     */
	public static Bitmap doFishEyeInverse(Bitmap src, int ix, int iy, double radius) {
		double r = radius; // radius of fish eye
		int w; // weight
		int vx, vy;
		double rp;
		// create new bitmap with the same settings as source bitmap
		Bitmap bmOut = Bitmap.createBitmap(src.getWidth(), src.getHeight(),
				src.getConfig());
		// color info
		int pixelColor;
		// image size
		int height = src.getHeight();
		int width = src.getWidth();
System.out.println("Height: " + height+ " width: " + width);
		w = 20;
//		r = ((width) < (height) ? (width) : (height)) / 2;
long t1 = System.currentTimeMillis();

		// scan through every pixel
		for (int y = 0; y < height; y++) {
			for (int x = 0; x < width; x++) {
				
				//calcul if (x,y) is in the circle (I, radius)
				if (r * r >= (x - ix) * (x - ix) + (y - iy) * (y - iy)) {
					rp = Math.sqrt((double) (w * w
							+ Math.pow((double) (x - ix), 2) + Math.pow(
							(double) (y - iy), 2)));

					vx = (int) ((rp * (x - ix)) / r + ix);
					vy = (int) ((rp * (y - iy)) / r + iy);

					if (0 <= vx && vx < width && 0 <= vy && vy < height) {
						// get one pixel
						pixelColor = src.getPixel(x, y);
						// set newly-inverted pixel to output image
						bmOut.setPixel(vx, vy, pixelColor);
					}
				} else {
					bmOut.setPixel(x, y, src.getPixel(x, y));
				}
			}
		}
	
long t2 = System.currentTimeMillis();
System.out.println("Fisheye with point time: " + (t2 - t1));
		// return final bitmap
		return bmOut;
	}
	
	
	
	 
    /** 
     * Create a fish eye effect to a Bitmap image
     */
	public static Bitmap liquify(Bitmap src, int ix, int iy, double r) {

		// create new bitmap with the same settings as source bitmap
		Bitmap bmOut = Bitmap.createBitmap(src.getWidth(), src.getHeight(),
				src.getConfig());
		int w = 5;
		// color info
		int pixelColor = src.getPixel(0, 0);
		int ox = ix - w, oy = iy;
		// image size
		int height = src.getHeight();
		int width = src.getWidth();
		System.out.println("Height: " + height + " width: " + width);
		long t1 = System.currentTimeMillis();
		// scan through every pixel
		for (int y = 0; y < height; y++) {
			for (int x = 0; x < width; x++) {
				// x, y in the circle
				double d = Math.sqrt((x - ox) * (x - ox) + (y - oy) * (y - oy));//distant^2
				double coef = (r-d)/r;
				if (r >= d) {
//					bmOut.setPixel(x, y, pixelColor);
					// if exist in the image original
					if (0 <= (x + ix - ox) && (x + ix - ox) < width
							&& 0 <= (y + iy - oy) && (y + iy - oy) < height) {
						// get one pixel
						pixelColor = src.getPixel((int)(x + (ix - ox) * coef),(int)( y + (iy - oy) * coef));
						// set newly-inverted pixel to output image
						bmOut.setPixel(x, y, pixelColor);
					}
				} else {
					bmOut.setPixel(x, y, src.getPixel(x, y));
				}
			}
		}
		long t2 = System.currentTimeMillis();
		System.out.println("Liquify time: " + (t2 - t1));
		// return final bitmap
		return bmOut;
	}
	
	
	
	// MorphStage method
	// Tao hieu ung bong mo giua 2 anh bitmap
	public static Bitmap morphStage(Bitmap startBitmap, Bitmap endBitmap,
			int numStages, int k) {
		
		Bitmap bmOut = Bitmap.createBitmap(startBitmap.getWidth(), startBitmap.getHeight(),
				startBitmap.getConfig());

		// color info
		int pixelColorStart;
		int pixelColorEnd;
		pixelColorStart = startBitmap.getPixel(0, 0);
		
		// image size
		int height = startBitmap.getHeight();
		int width = startBitmap.getWidth();
		
		
		// scan through every pixel
		for (int y = 0; y < height; y++) {
			for (int x = 0; x < width; x++) {
				pixelColorStart = startBitmap.getPixel(x, y);
				pixelColorEnd = endBitmap.getPixel(x, y);
				int A = Color.alpha(pixelColorStart)+ ((Color.alpha(pixelColorEnd) - Color.alpha(pixelColorStart)) / (numStages + 1)) * k;
				int R = Color.red(pixelColorStart) + ((Color.red(pixelColorEnd) - Color.red(pixelColorStart)) / (numStages + 1)) * k;
				int G = Color.green(pixelColorStart) + ((Color.green(pixelColorEnd) - Color.green(pixelColorStart)) / (numStages + 1)) * k;
				int B = Color.blue(pixelColorStart) + ((Color.blue(pixelColorEnd) - Color.blue(pixelColorStart)) / (numStages + 1)) * k;
				bmOut.setPixel(x, y, Color.argb(A, R, G, B));
			}
		}

		return bmOut;
	}// end of class Picture, put all new methods before this
	
	
	
	
	public static Bitmap affine(Bitmap src) {

		Bitmap bmOut = Bitmap.createBitmap(src.getWidth(), src.getHeight(),
				src.getConfig());
		// color info
		int pixelColor;
		// image size
		int height = src.getHeight();
		int width = src.getWidth();

		double h = 0.05;// value for transform
		double g = 0.07;// value for transform
		int qx;
		int qy;

		for (int y = 0; y < height; y++) {
			for (int x = 0; x < width; x++) {
				qx = (int)( x + h * y);
				qy = (int)( g * x + y);
				if (0 <= qx && qx < width && 0 <= qy && qy < height) {
					// get one pixel
					pixelColor = src.getPixel(qx, qy);
					// set newly-inverted pixel to output image
					bmOut.setPixel(x, y, pixelColor);
				}
			}
		}

		return bmOut;
	}
    
	
    
    public void detectFace(){
    	
    }
    
    
   
    public ImageProcessor(){
    }
    
//    public ImageProcessor(Bitmap image) {
//    	Mat tmp = new Mat(image.getHeight(),image.getWidth(), CvType.CV_8UC3, new Scalar(0));
//		Utils.bitmapToMat(image, tmp);
//    }
//    
//    public static Mat zero(Mat image){
//    	Mat img = new Mat(image.size(), CvType.CV_8UC3, new Scalar(0));
//		return img;
//    }
   
    
}
